package anc.server.tankshooter.network;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.sql.Connection;

import anc.server.tankshooter.App;
import anc.server.tankshooter.database.AccountManager;
import anc.server.tankshooter.database.Connect;
import anc.server.tankshooter.models.Account;

/**
 * Lớp tạo Thread để lắng nghe thông điệp cho mỗi Client
 * @author WolfNguyen
 *
 */
public class Player implements Runnable {

	static enum STATE{
		INIT,
		LOGIN,
		LISTENING,
		WAITING,
		PLAYING,
		FINISHED
	}
	public STATE mState;
	
	private Socket mSocket;
	private DataInputStream mDIS;
	private DataOutputStream mDOS;
	
	/**
	 * Biến kiểm tra tránh việc các tiến trình khác nhau cùng sử dụng DOS
	 */
	private boolean mIsWriting;
	
	private Account mAccount;
	private DataPacket thePacket;
	private Server mServer;
	private Connection mConnection;
	private AccountManager mAccountManager;
	private Match mMatch;
	
	public Player()
	{
		mState = STATE.INIT;
		mAccount = new Account();
		mConnection = Connect.getConnection();
		mAccountManager = new AccountManager(mConnection);
	}
	
	public Player(Socket socket, Server server) throws IOException
	{
		mSocket = socket;
		mServer = server;
		mState = STATE.LOGIN;
		mAccount = new Account();
		mConnection = Connect.getConnection();
		mAccountManager = new AccountManager(mConnection);
	}

	/**
	 * Phương thức Login
	 * @param loginInfo
	 * @return
	 */
	private boolean Login(String loginInfo)
	{
		String[] info = loginInfo.split(":", 2);
		mAccount = mAccountManager.get(info[0], info[1]);
		if(mAccount != null)
		{
			if(mServer.putInHashMap(mAccount.getAccountId(), this))
			{
				IOStream.SendInfo(mAccount, mDOS); // Goi thong tin dang nhap thanh cong.
				App.printString("Login success.");
				return true;
			}
			else 
			{
				IOStream.LoginFailed(mDOS, CommandList.SERVER_LOGIN_FAILED_SIGNED);
				App.printString("Username Loged.");
				return false;
				//Account Logged
				
			}
		}else
		{
			App.printString("mAccount == null");
		}
		IOStream.LoginFailed(mDOS, CommandList.SERVER_LOGIN_FAILED_WRONG_UP);
		return false;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Player.this.mDIS = new DataInputStream(mSocket.getInputStream());
			Player.this.mDOS = new DataOutputStream(mSocket.getOutputStream());
			
			while(mState == STATE.LOGIN)
			{
				try{
					thePacket = IOStream.ReadPacket(mDIS);
					if(thePacket.getCommand() == CommandList.CLIENT_LOGIN)
					{
						if(Login(thePacket.getValue()))
						{
							mState = STATE.LISTENING;
							Listen();
						}
					}// LOGIN
					
					if(thePacket.getCommand() == CommandList.CLIENT_SIGNUP)
					{
						String usname, pws, name, email;
						String[] info = thePacket.getValue().split(":");
						Account ac = new Account();
						usname = info[0]; pws = info[1]; name = info[2]; email = info[3];
						ac.setUsername(usname);
						ac.setPassword(pws);
						ac.setEmail(email);
						ac.setName(name);
						if(mAccountManager.add(ac) > -1)
						{
							IOStream.Send(CommandList.SERVER_SIGNUP_SUCCESS, "", mDOS);
						}else
						{
							IOStream.Send(CommandList.SERVER_SIGNUP_FAILED, "", mDOS);
						}
					}
					// SIGNUP
				} catch(IOException e)
				{
					ClientDisconnect();
				}		
			} 
			App.printString("Thread done.");
		} catch (IOException e) {
			
		}
		
	}
	
	/**
	 * Lắng nghe thông điệp từ Client khi chưa bắt đầu chơi game
	 */
	private void Listen()
	{
		while(mState == STATE.LISTENING)
		{
			try {
				thePacket = IOStream.ReadPacket(mDIS);
				switch (thePacket.getCommand())
				{
					case CommandList.CLIENT_ALL_ROOM:
						SendAllMatch(mServer.GetAllMatch());
						break;
						
					case CommandList.CLIENT_JOIN:
						int RoomIndex = Integer.parseInt(thePacket.getValue());
						if(mServer.ClientJoinRoom(RoomIndex, this))
						{
							mServer.SendMatchForAllClient();
							this.mMatch = mServer.GetMatch(RoomIndex);
						}
						else
						{
							Writing();
							IOStream.Send(CommandList.SERVER_JOIN_FAILED, thePacket.getValue(), mDOS);
							WriteDone();
						}
						break;
						
					case CommandList.CLIENT_LEAVE_ROOM:
						ClientLeaveRoom();
						break;
						
					case CommandList.CLIENT_START:
						int mapIndex = Integer.parseInt(thePacket.getValue());
						mMatch.StartGame(mapIndex);
						mServer.SendMatchForAllClient();
						break;
					case CommandList.CLIENT_STARTED:
						mState = STATE.PLAYING;
						Match();
						break;
					case CommandList.CLIENT_DISCONNECTED:
					 	
					 	break; 	
					default:
					break;
				}
			} catch (IOException e) {
				ClientDisconnect(); // ClientWaitingForReConnect() is better
			}
		}
	}
	
	/**
	 * Lắng nghe thông điệp từ Client khi chơi game
	 */
	private void Match()
	{
		while(mState == STATE.PLAYING)
		{
			try {
				thePacket = IOStream.ReadPacket(mDIS);
				switch (thePacket.getCommand())
				{
					case CommandList.CLIENT_RUN:
						App.printString("Client Run, ID: " + mAccount.getAccountId());
						mMatch.Run(thePacket.getValue(), mAccount.getAccountId());
						break;
					case CommandList.CLIENT_FIRE:
						App.printString("Client Fire, ID: " + mAccount.getAccountId());
						mMatch.Fire(mAccount.getAccountId(), thePacket.getValue());
						break;
					case CommandList.CLIENT_PAUSE:
						mMatch.Pause();
						break;
					case CommandList.CLIENT_RESUME:
						mMatch.Resume();
						break;
					case CommandList.CLIENT_PLAYER_GIVEUP:
						mMatch.ClientGiveUp(mAccount.getAccountId());
						break;
					case CommandList.CLIENT_PLAY_DONE:
						mState = STATE.LISTENING;
						break;
				}
			} catch (IOException e) {
				ClientDisconnect();
			}
		}
	}
	
	/**
	 * Gởi AllMatch cho Client này
	 */
	public void SendAllMatch(String allRoomString)
	{
		GetWriting();
		IOStream.Send(CommandList.SERVER_ALL_ROOM, allRoomString, mDOS);
		WriteDone();
	}
	
	/**
	 * Khi Client Join vào bàn chơi
	 */
	public void ClientJoinRoomSuccess(String room)
	{
		Writing();
		IOStream.Send(CommandList.SERVER_JOIN_SUCCESSED, room, mDOS);
		WriteDone();
	}
	
	/**
	 * Gởi thông điệp ClientLeaveRoom
	 */
	public void ClientLeaveRoom()
	{
		if(mMatch != null & mMatch.LeavePlayer(this))
		{
			mServer.SendMatchForAllClient();
			GetWriting();
			IOStream.Send(CommandList.SERVER_LEAVE_ROOM, mAccount.getAccountId()+ "", mDOS);
			WriteDone();
			mState = STATE.LISTENING;
			
		}
	}
	
	/**
	 * Khi Client ngưng kết nối
	 */
	private void ClientDisconnect()
	{
		mState = STATE.FINISHED;
		if(mMatch != null)
			mMatch.ClientDisconnect(this);
		if(mAccount.getAccountId()!= -1) 
			mServer.ClientDisconnect(mAccount.getAccountId());
		try {
			mSocket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			App.printString("Cant close Socket");
		}
		mAccount = null;
		mServer.SendMatchForAllClient();
	}

	/**
	 * Gởi thông điệp StartGame
	 * @param mapIndex
	 */
	public void StartGame(int mapIndex)
	{
		Writing();
		IOStream.Send(CommandList.SERVER_START, mapIndex + "" , mDOS);
		WriteDone();
		
	}
	
	/**
	 * 
	 */
	public void ClientGiveUp(int id)
	{
		GetWriting();
		IOStream.Send(CommandList.SERVER_PLAYER_GIVEUP, id + "", mDOS);
		WriteDone();
	}
	
	/**
	 * Pause
	 */
	public void Pause()
	{
		GetWriting();
		IOStream.Send(CommandList.SERVER_PAUSE,"", mDOS);
		WriteDone();
	}
	/**
	 * Resume
	 */
	public void Resume()
	{
		GetWriting();
		IOStream.Send(CommandList.SERVER_RESUME,"", mDOS);
		WriteDone();
	}
	
	/**
	 * Gởi thông điệp Run cho Client này
	 * @param value
	 */
	public void Run(String value, int id)
	{
		GetWriting();
		IOStream.Send(CommandList.SERVER_RUN, id + ":" + value, mDOS);
		WriteDone();
	}
	
	/**
	 * 
	 */
	public void Fire(int id, String xy)
	{
		GetWriting();
		IOStream.Send(CommandList.SERVER_FIRE, id + ":" + xy, mDOS);
		WriteDone();
	}
	
	/**
	 * Tạm dừng lắng nghe ở Thread này
	 * @throws InterruptedException
	 */
	public void PauseListening() throws InterruptedException
	{
		this.wait();
	}
	
	/**
	 * Lắng nghe lại
	 */
	public void ResumeListening()
	{
		this.notify();
	}
	
	
	public Account getAccount(){ return mAccount; }
	
	
	/**
	 * Chờ để lấy quyền Writing
	 */
	public void GetWriting()
	{
		while(IsWriting()){}
		Writing();
	}

	/**
	 * Thiết lập Writing, khi đang Writing, các tiến trình khác không được ghi lên DOS của Player này.
	 */
	public void Writing() { mIsWriting = true; }
	
	/**
	 * Ghi xong
	 */
	public void WriteDone() { mIsWriting = false; }
	
	/**
	 * Trả về biến kiểm tra có đang ghi hay không.
	 * @return
	 */
	public boolean IsWriting() { return mIsWriting; }
}
